#!/usr/bin/python2.4


import os
import sys

import constants
import dashboard as dashboard_module
from logger import logger, rpc_logger
import oracle as oracle_module
from rpc_stubs import afe, tko
from sql_query import sql
import test_plan as test_plan_module
import utils


class Dioxide(object):

  def __init__(self, test_plan_path, inspector_path=None,
               query_hosts=True, query_results=False):
    test_plans = test_plan_module.read_test_plans_from_file(test_plan_path)
    if not test_plans:
      raise AssertionError('missing test plan definition in %r' %
                           test_plan_path)
    self.test_plan = test_plans[0]
    self.oracle = oracle_module.Oracle(self.test_plan, inspector_path)
    self.test_results = self.oracle.test_results
    self.oracle.setup(query_hosts=query_hosts, query_results=query_results)
    self.dashboard = None

  def __repr__(self):
    results = '%s(name=%r, %d tests' % (type(self).__name__,
                                        self.test_plan.name,
                                        len(self.test_plan.tests))
    if self.test_plan.hosts is not None:
      results += ', %d hosts' % len(self.test_plan.hosts)
    if self.test_results.job_host_runs is not None:
      results += ', %d runs' % len(self.test_results.job_host_runs)
    results += ')'
    return results

  def __getattr__(self, name):
    if name in ('name', 'tests', 'hosts',
                'get_hosts', 'get_test', 'hostnames', 'create_job'):
      return getattr(self.test_plan, name)

    if name in ('inspector', 'job_host_runs',
                'host_job_results', 'host_results', 'host_job_status',
                'overall_stats', 'test_stats', 'host_stats',
                'overall_run_stats', 'test_run_stats', 'host_run_stats',
                'get_detailed_test_views', 'get_test_views_with_labels',
                'get_sql_test_views', 'get_sql_job_host_runs'):
      return getattr(self.test_results, name)

    raise AttributeError('%r object has no attribute %r' % (
                         type(self).__name__, name))

  def setup_dashboard(self, port=8000):
    self.dashboard = dashboard_module.Dashboard(self.oracle)
    self.dashboard.setup()
    self.dashboard.start(port)
    return self.dashboard

  def get_results(self, *args, **dargs):
    results = self.test_results.get_results(*args, **dargs)
    self.test_results.compute_stats()
    self.test_results.compute_run_stats()
    return results

  @staticmethod
  def set_notes(hosts, notes, update_objects=False):
    hostnames = [host.hostname for host in hosts]
    afe.set_host_attribute(constants.HA_NOTES, notes, hostname__in=hostnames)
    if update_objects:
      for host in hosts:
        host.set_notes(notes, commit=False)

  @staticmethod
  def set_triage_info(job_host_runs, triage_info, update_objects=False):
    set_test_view_idx = [(job_host_run.first_failed_test_view or
                          job_host_run.test_views[0]).test_idx
                         for job_host_run in job_host_runs]
    tko.set_test_attribute(constants.TA_TRIAGE_INFO, triage_info,
                           test_idx__in=set_test_view_idx)
    if update_objects:
      for job_host_run in job_host_runs:
        job_host_run.set_triage_info(triage_info, commit=False)

  @staticmethod
  def set_label(job_host_runs, label_to_set,
                must_fail=True, update_objects=False):
    if must_fail:
      job_tags = []
      for job_host_run in job_host_runs:
        if not job_host_run.first_failed_test_view:
          raise AssertionError("Unable to set label '%s' for '%s'" %
                               (label_to_set, job_host_run.job_tag))
        job_tags.append(job_host_run.job_tag)
    else:
      job_tags = [job_host_run.job_tag
                  for job_host_run in job_host_runs]

    for label in constants.ALL_TL_LABELS:
      if label == label_to_set:
        tko.test_label_add_tests(label, job_tag__in=job_tags)
      else:
        tko.test_label_remove_tests(label, job_tag__in=job_tags)

    if update_objects:
      for job_host_run in job_host_runs:
        job_host_run.set_label(label_to_set, must_fail=must_fail, commit=False)

  @staticmethod
  def mark_as_invalidated(job_host_runs, update_objects=False):
    return Dioxide.set_label(job_host_runs, constants.TL_INVALIDATED,
                             must_fail=False, update_objects=update_objects)

  @staticmethod
  def mark_as_resolved(job_host_runs, update_objects=False):
    return Dioxide.set_label(job_host_runs, constants.TL_RESOLVED,
                             must_fail=True, update_objects=update_objects)

  @staticmethod
  def mark_as_fail(job_host_runs, update_objects=False):
    return Dioxide.set_label(job_host_runs, constants.TL_NO_RERUN,
                             must_fail=True, update_objects=update_objects)

  @staticmethod
  def clear_labels(job_host_runs, update_objects=False):
    return Dioxide.set_label(job_host_runs, None,
                             must_fail=False, update_objects=update_objects)


dioxide = None


def main():
  dioxide_dargs = {}
  for i, arg in enumerate(('test_plan_path', 'inspector_path',
                           'query_hosts', 'query_results')):
    if i + 1 < len(sys.argv):
      if i + 1 <= 2:
        if sys.argv[i + 1] == 'None':
          dioxide_dargs[arg] = None
        else:
          dioxide_dargs[arg] = sys.argv[i + 1]
      else:
        dioxide_dargs[arg] = eval(sys.argv[i + 1])
    else:
      break
  if dioxide_dargs:
    global dioxide
    dioxide = Dioxide(**dioxide_dargs)


if __name__ == '__main__':
  if 'DIOXIDE_EXECED' in os.environ:
    del os.environ['DIOXIDE_EXECED']
  elif 'IPython' not in sys.modules:
    try:
      import IPython
    except ImportError:
      os.environ['DIOXIDE_EXECED'] = 'TRUE';
      os.execl('/usr/bin/python2.4', 'python2.4', '-i', *sys.argv)
    else:
      sys.argv.insert(0, sys.argv[0])
      IPython.Shell.start().mainloop()
      sys.exit()
  main()
